﻿@page "/pages/Contacts"

@using BlazorDownloadFile
@using CleanArchitecture.Blazor.Application.Features.Contacts.Caching
@using CleanArchitecture.Blazor.Application.Features.Contacts.DTOs
@using CleanArchitecture.Blazor.Application.Features.Contacts.Mappers
@using CleanArchitecture.Blazor.Application.Features.Contacts.Specifications
@using CleanArchitecture.Blazor.Application.Features.Contacts.Commands.Delete
@using CleanArchitecture.Blazor.Application.Features.Contacts.Commands.Import
@using CleanArchitecture.Blazor.Application.Features.Contacts.Queries.Export
@using CleanArchitecture.Blazor.Application.Features.Contacts.Queries.Pagination
@using CleanArchitecture.Blazor.Application.Features.Contacts.Commands.AddEdit
@using CleanArchitecture.Blazor.Server.UI.Pages.Contacts.Components

@inject IStringLocalizer<Contacts> L
@attribute [Authorize(Policy = Permissions.Contacts.View)]
<PageTitle>@Title</PageTitle>

<MudDataGrid ServerData="@(ServerReload)"
             FixedHeader="true"
             FixedFooter="true"
             @bind-RowsPerPage="_defaultPageSize"
             Height="calc(100vh - 330px)"
             Loading="@_loading"
             MultiSelection="true"
             T="ContactDto"
             SelectOnRowClick="false"
             RowClick="@(s=>OnView(s.Item))"
             @bind-SelectedItems="_selectedItems"
             @bind-SelectedItem="_currentDto"
             Hover="true" @ref="_table">
    <ToolBarContent>
        <MudStack Row Spacing="0" Class="flex-grow-1" Justify="Justify.SpaceBetween">
            <MudStack Row AlignItems="AlignItems.Start">
                <MudIcon Icon="@Icons.Material.Filled.Window" Size="Size.Large" />
                <MudStack Spacing="0">
                    <MudText Typo="Typo.subtitle2" Class="mb-2">@Title</MudText>
                    <MudEnumSelect Style="min-width:120px" TEnum="ContactListView" ValueChanged="OnChangedListView" Value="Query.ListView" Dense="true" Label="@L["List View"]">
                    </MudEnumSelect>
                </MudStack>
            </MudStack>
            <MudStack Spacing="0" AlignItems="AlignItems.End">
                <MudToolBar Dense WrapContent="true" Class="py-1 px-0">
                    <MudButton Disabled="@_loading"
                               OnClick="@(() => OnRefresh())"
                               StartIcon="@Icons.Material.Outlined.Refresh">
                        @ConstantString.Refresh
                    </MudButton>
                    @if (_canCreate)
                    {
                        <MudButton StartIcon="@Icons.Material.Outlined.Add"
                                   OnClick="OnCreate">
                            @ConstantString.New
                        </MudButton>
                    }
                    <MudMenu TransformOrigin="Origin.BottomRight" AnchorOrigin="Origin.BottomRight" EndIcon="@Icons.Material.Filled.MoreVert" Label="@ConstantString.More">
                        @if (_canCreate)
                        {
                            <MudMenuItem Disabled="@(_selectedItems.Count != 1)" OnClick="OnClone">@ConstantString.Clone</MudMenuItem>
                        }
                        @if (_canDelete)
                        {
                            <MudMenuItem Disabled="@(!(_selectedItems.Count > 0))"
                                         OnClick="OnDeleteChecked">
                                @ConstantString.Delete
                            </MudMenuItem>
                        }
                        @if (_canExport)
                        {
                            <MudMenuItem Disabled="@_exporting"
                                         OnClick="OnExport">
                                @ConstantString.Export
                            </MudMenuItem>
                        }
                        @if (_canImport)
                        {
                            <MudMenuItem>
                                <MudFileUpload T="IBrowserFile" FilesChanged="OnImportData" Accept=".xlsx">
                                    <ActivatorContent>
                                        <MudButton Class="pa-0 ma-0" Style="font-weight:400;text-transform:none;"
                                                   Variant="Variant.Text"
                                                   Disabled="@_uploading">
                                            @ConstantString.Import
                                        </MudButton>
                                    </ActivatorContent>
                                </MudFileUpload>
                            </MudMenuItem>
                        }
                    </MudMenu>
                </MudToolBar>
                <MudStack Row Spacing="1">
                    @if (_canSearch)
                    {
                        <MudTextField T="string" ValueChanged="@(s => OnSearch(s))" Value="@Query.Keyword" Placeholder="@ConstantString.Search" Adornment="Adornment.End"
                                      AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small">
                        </MudTextField>
                    }
                </MudStack>
            </MudStack>
        </MudStack>
    </ToolBarContent>
    <Columns>
        <SelectColumn ShowInFooter="false"></SelectColumn>
        <TemplateColumn HeaderStyle="width:60px" Title="@ConstantString.Actions" Sortable="false">
            <CellTemplate>
                @if (_canEdit || _canDelete)
                {
                    <MudMenu Icon="@Icons.Material.Filled.Edit" Variant="Variant.Filled" Size="Size.Small"
                             Dense="true"
                             EndIcon="@Icons.Material.Filled.KeyboardArrowDown" IconColor="Color.Info" AnchorOrigin="Origin.CenterLeft">
                        @if (_canEdit)
                        {
                            <MudMenuItem OnClick="@(()=>OnEdit(context.Item))">@ConstantString.Edit</MudMenuItem>
                        }
                        @if (_canDelete)
                        {
                            <MudMenuItem OnClick="@(()=>OnDelete(context.Item))">@ConstantString.Delete</MudMenuItem>
                        }
                    </MudMenu>
                }
                else
                {
                    <MudIconButton Variant="Variant.Filled"
                                   Disabled="true"
                                   Icon="@Icons.Material.Filled.DoNotTouch"
                                   Size="Size.Small"
                                   Color="Color.Surface">
                        @ConstantString.NoAllowed
                    </MudIconButton>
                }
            </CellTemplate>
        </TemplateColumn>

        <PropertyColumn Property="x => x.Name" Title="@L[_currentDto.GetMemberDescription(x=>x.Name)]">
            <CellTemplate>
                <div class="d-flex flex-column">
                    <MudText Typo="Typo.body1">@context.Item.Name</MudText>
                    <MudText Typo="Typo.body2" Class="mud-text-secondary">@context.Item.Description</MudText>
                </div>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Email" Title="@L[_currentDto.GetMemberDescription(x=>x.Email)]" />
        <PropertyColumn Property="x => x.PhoneNumber" Title="@L[_currentDto.GetMemberDescription(x=>x.PhoneNumber)]" />
        <PropertyColumn Property="x => x.Country" Title="@L[_currentDto.GetMemberDescription(x=>x.Country)]" />

    </Columns>
    
    <PagerContent>
        <MudDataGridPager PageSizeOptions="@(new int[]{10,15,30,50,100,500,1000})" />
    </PagerContent>
</MudDataGrid>



@code {
    public string? Title { get; private set; }
    private int _defaultPageSize = 15;
    private HashSet<ContactDto> _selectedItems = new HashSet<ContactDto>();
    private MudDataGrid<ContactDto> _table = default!;
    private ContactDto _currentDto = new();
    private bool _loading;
    private bool _uploading;
    private bool _exporting;
    [CascadingParameter]
    private Task<AuthenticationState> AuthState { get; set; } = default!;
    [CascadingParameter]
    private UserProfile? UserProfile { get; set; }
    [CascadingParameter(Name = "LocalTimezoneOffset")]
    private TimeSpan _localTimezoneOffset { get; set; }

    private ContactsWithPaginationQuery Query { get; set; } = new();
    [Inject]
    private IBlazorDownloadFileService BlazorDownloadFileService { get; set; } = null!;
    private bool _canSearch;
    private bool _canCreate;
    private bool _canEdit;
    private bool _canDelete;
    private bool _canImport;
    private bool _canExport;

    protected override async Task OnInitializedAsync()
    {
        Title = L[_currentDto.GetClassDescription()];
        var state = await AuthState;
        _canCreate = (await AuthService.AuthorizeAsync(state.User, Permissions.Contacts.Create)).Succeeded;
        _canSearch = (await AuthService.AuthorizeAsync(state.User, Permissions.Contacts.Search)).Succeeded;
        _canEdit = (await AuthService.AuthorizeAsync(state.User, Permissions.Contacts.Edit)).Succeeded;
        _canDelete = (await AuthService.AuthorizeAsync(state.User, Permissions.Contacts.Delete)).Succeeded;
        _canImport = (await AuthService.AuthorizeAsync(state.User, Permissions.Contacts.Import)).Succeeded;
        _canExport = (await AuthService.AuthorizeAsync(state.User, Permissions.Contacts.Export)).Succeeded;
    }
    private async Task<GridData<ContactDto>> ServerReload(GridState<ContactDto> state)
    {
        try
        {
            _loading = true;
            Query.CurrentUser = UserProfile;
            Query.OrderBy = state.SortDefinitions.FirstOrDefault()?.SortBy ?? "Id";
            Query.SortDirection = state.SortDefinitions.FirstOrDefault()?.Descending ?? true ? SortDirection.Descending.ToString() : SortDirection.Ascending.ToString();
            Query.PageNumber = state.Page + 1;
            Query.PageSize = state.PageSize;
            Query.LocalTimezoneOffset = _localTimezoneOffset;
            var result = await Mediator.Send(Query).ConfigureAwait(false);
            return new GridData<ContactDto>() { TotalItems = result.TotalItems, Items = result.Items };
        }
        finally
        {
            _loading = false;
        }

    }
    private async Task OnSearch(string text)
    {
        _selectedItems = new();
        Query.Keyword = text;
        await _table.ReloadServerData();
    }
    private async Task OnChangedListView(ContactListView listview)
    {
        Query.ListView = listview;
        await _table.ReloadServerData();
    }
    private async Task OnRefresh()
    {
        ContactCacheKey.Refresh();
        _selectedItems = new();
        Query.Keyword = string.Empty;
        await _table.ReloadServerData();
    }
    private async Task ShowEditFormDialog(string title, AddEditContactCommand command)
    {
        var parameters = new DialogParameters<ContactFormDialog>
            {
                { x=>x.model,command },
            };
        var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
        var dialog = DialogService.Show<ContactFormDialog>(title, parameters, options);
        var state = await dialog.Result;

        if (state != null && !state.Canceled)
        {
            await _table.ReloadServerData();
            _selectedItems.Clear();
        }
    }
    private void OnView(ContactDto dto)
    {
        Navigation.NavigateTo($"/pages/Contacts/view/{dto.Id}");
    }
    private async Task OnCreate()
    {
        var command = new AddEditContactCommand();
        await ShowEditFormDialog(string.Format(ConstantString.CreateAnItem, L["Contact"]), command);
    }
    private async Task OnClone()
    {
        var dto = _selectedItems.First();
        var command = ContactMapper.CloneFromDto(dto);
        await ShowEditFormDialog(string.Format(ConstantString.CreateAnItem, L["Contact"]), command);
    }
    private async Task OnEdit(ContactDto dto)
    {
        Navigation.NavigateTo($"/pages/Contacts/edit/{dto.Id}");
    }

    private async Task OnDelete(ContactDto dto)
    {
        var contentText = string.Format(ConstantString.DeleteConfirmation, dto.Name);
        var command = new DeleteContactCommand(new int[] { dto.Id });
        await DialogServiceHelper.ShowDeleteConfirmationDialogAsync(command, ConstantString.DeleteConfirmationTitle, contentText, async () =>
        {
            await InvokeAsync(async () =>
            {
                await _table.ReloadServerData();
                _selectedItems.Clear();
            });
        });
    }

    private async Task OnDeleteChecked()
    {
        var contentText = string.Format(ConstantString.DeleteConfirmWithSelected, _selectedItems.Count);
        var command = new DeleteContactCommand(_selectedItems.Select(x => x.Id).ToArray());
        await DialogServiceHelper.ShowDeleteConfirmationDialogAsync(command, ConstantString.DeleteConfirmationTitle, contentText, async () =>
        {
            await InvokeAsync(async () =>
            {
                await _table.ReloadServerData();
                _selectedItems.Clear();
            });
        });
    }

    private async Task OnExport()
    {
        _exporting = true;
        var request = new ExportContactsQuery()
            {
                Keyword = Query.Keyword,
                CurrentUser = UserProfile,
                ListView = Query.ListView,
                OrderBy = _table.SortDefinitions.Values.FirstOrDefault()?.SortBy ?? "Id",
                SortDirection = (_table.SortDefinitions.Values.FirstOrDefault()?.Descending ?? true) ? SortDirection.Descending.ToString() : SortDirection.Ascending.ToString()
            };
        var result = await Mediator.Send(request);
        await result.MatchAsync<byte[]>(
            async data =>
            {
                var downloadresult = await BlazorDownloadFileService.DownloadFile($"{L["Contacts"]}.xlsx", result.Data, contentType: "application/octet-stream");
                Snackbar.Add($"{ConstantString.ExportSuccess}", MudBlazor.Severity.Info);
                return data;
            },
            errors =>
            {
                Snackbar.Add($"{errors}", MudBlazor.Severity.Error);
                return Task.FromResult(Array.Empty<byte>());
            });
        _exporting = false;
    }
    private async Task OnImportData(IBrowserFile file)
    {
        _uploading = true;
        var stream = new MemoryStream();
        await file.OpenReadStream().CopyToAsync(stream);
        var command = new ImportContactsCommand(file.Name, stream.ToArray());
        var result = await Mediator.Send(command);
        await result.MatchAsync(
            async data =>
            {
                await _table.ReloadServerData();
                Snackbar.Add($"{ConstantString.ImportSuccess}", MudBlazor.Severity.Info);
                return data;
            }, errors =>
            {
                Snackbar.Add($"{errors}", MudBlazor.Severity.Error);
                return Task.FromResult(0);
            });
        _uploading = false;
    }

}
